﻿using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Video;
using UObject = UnityEngine.Object;

namespace FutureCore
{
    [Serializable]
    public class AssetLoader
    {
        #region Field
        private const bool IsResourcesMode = AppConst.IsResourcesMode_Default;
        private const bool IsSyncMode = AppConst.IsSyncLoadMode_Default;

        [SerializeField]
        private List<string> loadedPaths;
        #endregion

        #region Init
        public AssetLoader()
        {
            loadedPaths = new List<string>();
        }
        #endregion

        #region Load
        public void Load<T>(string assetPath, LoadedObjectFunc func, bool isSync = IsSyncMode) where T : UObject
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                T cacheAsset = GetCacheAsset<T>(assetPath);
                func(cacheAsset);
                return;
            }

            ResMgr.Instance.Load<T>(assetPath, func, isSync);
            AddLoadedPath(assetPath);
        }

        public void LoadObject(string assetPath, LoadedObjectFunc func, bool isSync = IsSyncMode)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                UObject cacheAsset = GetCacheAsset<UObject>(assetPath);
                func(cacheAsset);
                return;
            }

            ResMgr.Instance.LoadObject(assetPath, func, isSync);
            AddLoadedPath(assetPath);
        }

        public void LoadPrefab(string assetPath, LoadedGameObjectFunc func, bool isSync = IsSyncMode)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                GameObject cacheAsset = GetCacheAsset<GameObject>(assetPath);
                func(cacheAsset);
                return;
            }

            ResMgr.Instance.LoadPrefab(assetPath, func, isSync);
            AddLoadedPath(assetPath);
        }

        public void LoadGameObject(string assetPath, LoadedGameObjectFunc func, bool isSync = IsSyncMode)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                GameObject cacheAsset = GetCacheAsset<GameObject>(assetPath);
                GameObject gameObject = EngineUtil.Instantiate(cacheAsset);
                func(gameObject);
                return;
            }

            ResMgr.Instance.LoadGameObject(assetPath, func, isSync);
            AddLoadedPath(assetPath);
        }

        public void LoadSprite(string assetPath, LoadedSpriteFunc func, bool isSync = IsSyncMode)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                Sprite cacheAsset = null;
                if (IsResourcesMode)
                {
                    cacheAsset = GetCacheAsset<GameObject>(assetPath).GetComponent<SpriteRenderer>().sprite;
                }
                else
                {
                    cacheAsset = GetCacheAsset<Sprite>(assetPath);
                }
                func(cacheAsset);
                return;
            }

            ResMgr.Instance.LoadSprite(assetPath, func, isSync);
            AddLoadedPath(assetPath);
        }

        public void LoadTexture(string assetPath, LoadedTextureFunc func, bool isSync = IsSyncMode)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                Texture cacheAsset = GetCacheAsset<Texture>(assetPath);
                func(cacheAsset);
                return;
            }

            ResMgr.Instance.LoadTexture(assetPath, func, isSync);
            AddLoadedPath(assetPath);
        }

        public void LoadMaterial(string assetPath, LoadedMaterialFunc func, bool isSync = IsSyncMode)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                Material cacheAsset = GetCacheAsset<Material>(assetPath);
                func(cacheAsset);
                return;
            }

            ResMgr.Instance.LoadMaterial(assetPath, func, isSync);
            AddLoadedPath(assetPath);
        }

        public void LoadShader(string assetPath, LoadedShaderFunc func, bool isSync = IsSyncMode)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                Shader cacheAsset = GetCacheAsset<Shader>(assetPath);
                func(cacheAsset);
                return;
            }

            ResMgr.Instance.LoadShader(assetPath, func, isSync);
            AddLoadedPath(assetPath);
        }

        public void LoadAudioClip(string assetPath, LoadedAudioClipFunc func, bool isSync = IsSyncMode)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                AudioClip cacheAsset = GetCacheAsset<AudioClip>(assetPath);
                func(cacheAsset);
                return;
            }

            ResMgr.Instance.LoadAudioClip(assetPath, func, isSync);
            AddLoadedPath(assetPath);
        }

        public void LoadTextAsset(string assetPath, LoadedTextAssetFunc func, bool isSync = IsSyncMode)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                TextAsset cacheAsset = GetCacheAsset<TextAsset>(assetPath);
                func(cacheAsset);
                return;
            }

            ResMgr.Instance.LoadTextAsset(assetPath, func, isSync);
            AddLoadedPath(assetPath);
        }

        public void LoadVideoClip(string assetPath, LoadedVideoClipFunc func, bool isSync = IsSyncMode)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                VideoClip cacheAsset = GetCacheAsset<VideoClip>(assetPath);
                func(cacheAsset);
                return;
            }

            ResMgr.Instance.LoadVideoClip(assetPath, func, isSync);
            AddLoadedPath(assetPath);
        }
        #endregion

        #region SyncLoad
        public T SyncLoad<T>(string assetPath) where T : UObject
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                T cacheAsset = GetCacheAsset<T>(assetPath);
                return cacheAsset;
            }

            T asset = ResMgr.Instance.SyncLoad<T>(assetPath);
            AddLoadedPath(assetPath);
            return asset;
        }

        public UObject SyncLoadObject(string assetPath)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                UObject cacheAsset = GetCacheAsset<UObject>(assetPath);
                return cacheAsset;
            }

            UObject asset = ResMgr.Instance.SyncLoadObject(assetPath);
            AddLoadedPath(assetPath);
            return asset;
        }

        public GameObject SyncLoadPrefab(string assetPath)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                GameObject cacheAsset = GetCacheAsset<GameObject>(assetPath);
                return cacheAsset;
            }

            GameObject asset = ResMgr.Instance.SyncLoadPrefab(assetPath);
            AddLoadedPath(assetPath);
            return asset;
        }

        public GameObject SyncLoadGameObject(string assetPath)
        {
            GameObject prefab = SyncLoadPrefab(assetPath);
            GameObject gameObject = EngineUtil.Instantiate(prefab);
            return gameObject;
        }

        public Sprite SyncLoadSprite(string assetPath)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                Sprite cacheAsset = null;
                if (IsResourcesMode)
                {
                    cacheAsset = GetCacheAsset<GameObject>(assetPath).GetComponent<SpriteRenderer>().sprite;
                }
                else
                {
                    cacheAsset = GetCacheAsset<Sprite>(assetPath);
                }
                return cacheAsset;
            }

            Sprite asset = ResMgr.Instance.SyncLoadSprite(assetPath);
            AddLoadedPath(assetPath);
            return asset;
        }

        public Texture SyncLoadTexture(string assetPath)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                Texture cacheAsset = GetCacheAsset<Texture>(assetPath);
                return cacheAsset;
            }

            Texture asset = ResMgr.Instance.SyncLoadTexture(assetPath);
            AddLoadedPath(assetPath);
            return asset;
        }

        public Material SyncLoadMaterial(string assetPath)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                Material cacheAsset = GetCacheAsset<Material>(assetPath);
                return cacheAsset;
            }

            Material asset = ResMgr.Instance.SyncLoadMaterial(assetPath);
            AddLoadedPath(assetPath);
            return asset;
        }

        public Shader SyncLoadShader(string assetPath)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                Shader cacheAsset = GetCacheAsset<Shader>(assetPath);
                return cacheAsset;
            }

            Shader asset = ResMgr.Instance.SyncLoadShader(assetPath);
            AddLoadedPath(assetPath);
            return asset;
        }

        public AudioClip SyncLoadAudioClip(string assetPath)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                AudioClip cacheAsset = GetCacheAsset<AudioClip>(assetPath);
                return cacheAsset;
            }

            AudioClip asset = ResMgr.Instance.SyncLoadAudioClip(assetPath);
            AddLoadedPath(assetPath);
            return asset;
        }

        public TextAsset SyncLoadTextAsset(string assetPath)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                TextAsset cacheAsset = GetCacheAsset<TextAsset>(assetPath);
                return cacheAsset;
            }

            TextAsset asset = ResMgr.Instance.SyncLoadTextAsset(assetPath);
            AddLoadedPath(assetPath);
            return asset;
        }

        public VideoClip SyncLoadVideoClip(string assetPath)
        {
            if (ResMgr.Instance.HasCacheAsset(assetPath))
            {
                VideoClip cacheAsset = GetCacheAsset<VideoClip>(assetPath);
                return cacheAsset;
            }

            VideoClip asset = ResMgr.Instance.SyncLoadVideoClip(assetPath);
            AddLoadedPath(assetPath);
            return asset;
        }
        #endregion

        #region CacheLoad
        public GameObject GetCacheGameObject(string assetPath)
        {
            GameObject prefab = GetCacheAsset<GameObject>(assetPath);
            return EngineUtil.Instantiate(prefab);
        }

        public T GetInCacheAsset<T>(string assetPath) where T : UObject
        {
            return ResMgr.Instance.GetInCacheAsset<T>(assetPath);
        }

        public T GetCacheAsset<T>(string assetPath) where T : UObject
        {
            if (!ResMgr.Instance.HasCacheAsset(assetPath))
            {
                UObject asset = SyncLoad<T>(assetPath);
                ResMgr.Instance.AddCacheAsset(assetPath, asset);
            }
            return ResMgr.Instance.GetInCacheAsset<T>(assetPath);
        }
        #endregion

        #region Func
        private void AddLoadedPath(string path)
        {
            loadedPaths.Add(path);
        }

        public void ClearCache()
        {
            for (int i = 0; i < loadedPaths.Count; i++)
            {
                ResMgr.Instance.DelCacheAsset(loadedPaths[i]);
            }
        }

        public void Release()
        {
            if (!IsResourcesMode)
            {
                for (int i = 0; i < loadedPaths.Count; i++)
                {
                    ResMgr.Instance.Unload(loadedPaths[i]);
                }
            }
            loadedPaths.Clear();
        }

        public void Dispose()
        {
            loadedPaths.Clear();
        }
        #endregion
    }
}